

#include <cegfx/cematerialmanager.h>
#include <cegfx/cexml.h>
#include <cegfx/cexmlhelper.h>
#include <libxml/parser.h>
#include <libxml/tree.h>


ceMaterialManager::ceMaterialManager (LPDIRECT3DDEVICE9 device)
	: device (device)
{
	IQF_CONSTRUCT;
}

ceMaterialManager::~ceMaterialManager ()
{
}

IQF_IMPLEMENTATION_BEGIN(ceMaterialManager);
IQF_IMPLEMENTATION_END ();



bool ceMaterialManager::LoadMaterial (const char* filename)
{
	LIBXML_TEST_VERSION;
	xmlDoc* doc = xmlReadFile(filename, 0, 0);
	ceXmlElement* elem = new ceXmlElement (xmlDocGetRootElement (doc));
	if (strcmp (elem->GetTagName (), "material") != 0)
	{
		return false;
	}


	ceXmlElement* dataElem = elem->GetChild ("data");
	ceXmlElement* colorsElem = elem->GetChild ("colors");
	ceXmlElement* mapsElem = elem->GetChild ("maps");

	ceSmartPtr spData (dataElem), spColors (colorsElem), spMaps (mapsElem);

	if (!dataElem || !colorsElem || !mapsElem)
	{
		return false;
	}

	ceMaterial* material = new ceMaterial ();

	OpenData (dataElem);
	LoadColors (colorsElem, material);
	LoadMaps (mapsElem, material);

	MaterialData matData;
	ZeroMemory (&matData, sizeof (MaterialData));
	matData.Material = material;
	matData.Filename = _strdup (filename);
	matData.Name = _strdup (elem->GetAttribute ("name"));

	materials.push_back (matData);

	return true;
}

void ceMaterialManager::RegisterEffect (const char* filename, const char* effectname)
{
	EffectData data;
	ZeroMemory(&data, sizeof (EffectData));
	data.Filename = _strdup (filename);
	data.Name = _strdup (effectname);

	effects.push_back (data);
}


void ceMaterialManager::OpenData (ceXmlElement* element)
{
	unsigned nc = element->GetNumberOfChildren ();
	for (unsigned i=0; i<nc; i++)
	{
		ceXmlElement* dataElem = element->GetChild (i);
		ceSmartPtr sm (dataElem);

		if (strcmp (dataElem->GetTagName (), "effect") == 0)
		{
			// read the effect
			if (!HasEffect (dataElem->GetAttribute ("name")))
			{
				EffectData effect;
				ZeroMemory(&effect, sizeof (EffectData));
				effect.Name = _strdup(dataElem->GetAttribute ("name"));
				effect.Filename = _strdup (dataElem->GetAttribute ("filename"));
				effects.push_back (effect);
			}
		}
		else if (strcmp (dataElem->GetTagName (), "texture") == 0)
		{
			// read the texture
			if (!HasTexture (dataElem->GetAttribute ("name")))
			{
				TextureData texture;
				ZeroMemory(&texture, sizeof (TextureData));
				texture.Name = _strdup (dataElem->GetAttribute ("name"));
				texture.Filename = _strdup (dataElem->GetAttribute ("filename"));
				textures.push_back (texture);
			}
		}
	}
}

void ceMaterialManager::LoadColors (ceXmlElement* element, ceMaterial* material)
{
	unsigned nc = element->GetNumberOfChildren ();
	for (unsigned i=0; i<nc; i++)
	{
		ceXmlElement* elem = element->GetChild (i);
		if (strcmp (elem->GetTagName (), "ambient") == 0)
		{
			material->SetAmbient (D3DXCOLOR (
				elem->GetFloatAttribute ("r"),
				elem->GetFloatAttribute ("g"),
				elem->GetFloatAttribute ("b"),
				elem->GetFloatAttribute ("a")));
		}
		else if (strcmp (elem->GetTagName (), "diffuse") == 0)
		{
			material->SetDiffuse (D3DXCOLOR (
				elem->GetFloatAttribute ("r"),
				elem->GetFloatAttribute ("g"),
				elem->GetFloatAttribute ("b"),
				elem->GetFloatAttribute ("a")));
		}
		else if (strcmp (elem->GetTagName (), "specular") == 0)
		{
			material->SetSpecular (D3DXCOLOR (
				elem->GetFloatAttribute ("r"),
				elem->GetFloatAttribute ("g"),
				elem->GetFloatAttribute ("b"),
				elem->GetFloatAttribute ("a")));
		}
		else if (strcmp (elem->GetTagName (), "emissive") == 0)
		{
			material->SetEmissive (D3DXCOLOR (
				elem->GetFloatAttribute ("r"),
				elem->GetFloatAttribute ("g"),
				elem->GetFloatAttribute ("b"),
				elem->GetFloatAttribute ("a")));
		}
		else if (strcmp (elem->GetTagName (), "power") == 0)
		{
			material->SetPower (elem->GetFloatAttribute ("value"));
		}
	}
}

void ceMaterialManager::LoadMaps (ceXmlElement* element, ceMaterial* material)
{
	unsigned nc = element->GetNumberOfChildren ();
	for (unsigned i=0; i<nc; i++)
	{
		ceXmlElement* elem = element->GetChild (i);
		if (strcmp (elem->GetTagName (), "effect") == 0)
		{
			ceEffect* effect = GetEffect (elem->GetAttribute ("name"));
			material->SetEffect (effect);
		}
		else if (strcmp (elem->GetTagName (), "diffuse") == 0)
		{
			LPDIRECT3DTEXTURE9 txt = GetTexture (elem->GetAttribute ("name"));
			material->SetDiffuseTexture (txt);
		}
		else if (strcmp (elem->GetTagName (), "specular") == 0)
		{
			LPDIRECT3DTEXTURE9 txt = GetTexture (elem->GetAttribute ("name"));
			material->SetSpecularTexture (txt);
		}
		else if (strcmp (elem->GetTagName (), "bump") == 0)
		{
			LPDIRECT3DTEXTURE9 txt = GetTexture (elem->GetAttribute ("name"));
			material->SetBumpTexture (txt);
		}
	}
}

ceMaterial* ceMaterialManager::GetMaterial (const char* name)
{
	std::list<MaterialData>::iterator it;
	for (it = materials.begin (); it != materials.end (); it++)
	{
		MaterialData& mat = *it;
		if (strcmp (mat.Name, name) == 0)
		{
			return mat.Material;
		}
	}
	return 0;
}


ceEffect* ceMaterialManager::GetEffect (const char* name)
{
	std::list<EffectData>::iterator it;
	for (it = effects.begin (); it != effects.end (); it++)
	{
		EffectData& effect = *it;
		if (strcmp (effect.Name, name) == 0)
		{
			if (!effect.Effect)
			{
				LPD3DXEFFECT leffect;
				LPD3DXBUFFER ce;
				if (FAILED(D3DXCreateEffectFromFileA(device, effect.Filename, 0, 0, 0, 0, &leffect, &ce))) 
				{
					unsigned size = ce->GetBufferSize ();
					char* buffer = new char [size+1];
					memcpy (buffer, ce->GetBufferPointer (), size);
					buffer[size] = '\0';
					printf ("%s\n", buffer);
					delete [] buffer;
					return 0;
				}
				effect.Effect = new ceEffect ();
				effect.Effect->SetEffect (leffect);
			}
			return effect.Effect;
		}
	}

	return 0;
}

LPDIRECT3DTEXTURE9 ceMaterialManager::GetTexture(const char* name)
{
	printf ("ceMaterialManager::GetTexture (%s)\n", name);
	std::list<TextureData>::iterator it;
	for (it = textures.begin (); it != textures.end (); it++)
	{
		TextureData& texture = *it;
		if (strcmp (texture.Name, name) == 0)
		{
			if (!texture.Texture)
			{
				if (FAILED(D3DXCreateTextureFromFileA (device, texture.Filename, &texture.Texture)))
				{
					printf ("  unable to load texture: %s\n", texture.Filename);
					return 0;
				}
			}
			return texture.Texture;
		}
	}

	return 0;
}

bool ceMaterialManager::HasTexture (const char* name)
{
	std::list<TextureData>::iterator it;
	for (it = textures.begin (); it != textures.end (); it++)
	{
		TextureData& txt = *it;
		if (strcmp (txt.Name, name) == 0)
		{
			return true;
		}
	}
	return false;
}

bool ceMaterialManager::HasEffect (const char* name)
{
	std::list<EffectData>::iterator it;
	for (it = effects.begin (); it != effects.end (); it++)
	{
		EffectData& effect = *it;
		if (strcmp (effect.Name, name) == 0)
		{
			return true;
		}
	}
	return false;
}
